/*

 * Licensed to the Apache Software Foundation (ASF) under one

 * or more contributor license agreements.  See the NOTICE file

 * distributed with this work for additional information

 * regarding copyright ownership.  The ASF licenses this file

 * to you under the Apache License, Version 2.0 (the

 * "License"); you may not use this file except in compliance

 * with the License.  You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

package com.bff.gaia.unified.sdk.coders;



/**

 * The exception thrown when a {@link CoderRegistry} or {@link CoderProvider} cannot provide a

 * {@link Coder} that has been requested.

 */

public class CannotProvideCoderException extends Exception {

  private final ReasonCode reason;



  public CannotProvideCoderException(String message) {

    this(message, ReasonCode.UNKNOWN);

  }



  public CannotProvideCoderException(String message, ReasonCode reason) {

    super(message);

    this.reason = reason;

  }



  public CannotProvideCoderException(String message, Throwable cause) {

    this(message, cause, ReasonCode.UNKNOWN);

  }



  public CannotProvideCoderException(String message, Throwable cause, ReasonCode reason) {

    super(message, cause);

    this.reason = reason;

  }



  public CannotProvideCoderException(Throwable cause) {

    this(cause, ReasonCode.UNKNOWN);

  }



  public CannotProvideCoderException(Throwable cause, ReasonCode reason) {

    super(cause);

    this.reason = reason;

  }



  /** @return the reason that Coder inference failed. */

  public ReasonCode getReason() {

    return reason;

  }



  /**

   * Returns the inner-most {@link CannotProvideCoderException} when they are deeply nested.

   *

   * <p>For example, if a coder for {@code List<KV<Integer, Whatsit>>} cannot be provided because

   * there is no known coder for {@code Whatsit}, the root cause of the exception should be a

   * CannotProvideCoderException with details pertinent to {@code Whatsit}, suppressing the

   * intermediate layers.

   */

  public Throwable getRootCause() {

    Throwable cause = getCause();

    if (cause == null) {

      return this;

    } else if (!(cause instanceof CannotProvideCoderException)) {

      return cause;

    } else {

      return ((CannotProvideCoderException) cause).getRootCause();

    }

  }



  /** Indicates the reason that {@link Coder} inference failed. */

  public enum ReasonCode {

    /**

     * The reason a coder could not be provided is unknown or does not have an established {@link

     * ReasonCode}.

     */

    UNKNOWN,



    /**

     * The reason a coder could not be provided is type erasure, for example when requesting coder

     * inference for a {@code List<T>} where {@code T} is unknown.

     */

    TYPE_ERASURE,



    /**

     * The reason a coder could not be provided is because the type variable {@code T} is over

     * specified with multiple incompatible coders.

     */

    OVER_SPECIFIED

  }

}